package com.lst.tools.quartz.service.impl;

import com.lst.tools.basics.function.OneParamFunction;
import com.lst.tools.basics.utils.ValidatorUtils;
import com.lst.tools.quartz.service.ITaskService;
import com.lst.tools.quartz.vo.EditJobParam;
import com.lst.tools.quartz.vo.TaskInfoVo;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

/**
 * 定时任务业务层
 *
 * @author: Frank
 * @date: 2019/09/24 13:51
 * @version: V1.0
 */
@Service
public class TaskService implements ITaskService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskService.class);

    @Autowired(required = false)
    private Scheduler scheduler;

    @Override
    public List<TaskInfoVo> list() {
        List<TaskInfoVo> list = new ArrayList<>();
        try {
            TaskInfoVo info = null;
            for (String groupJob : scheduler.getJobGroupNames()) {
                for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.groupEquals(groupJob))) {
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    for (Trigger trigger : triggers) {
                        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                        JobDetail jobDetail = scheduler.getJobDetail(jobKey);

                        info = new TaskInfoVo();
                        if (trigger instanceof CronTrigger) {
                            CronTrigger cronTrigger = (CronTrigger) trigger;
                            info.setCronExpression(cronTrigger.getCronExpression());
                            info.setCreateTime(cronTrigger.getDescription());
                        }
                        info.setJobClassName(jobKey.getName());
                        info.setJobGroup(jobKey.getGroup());
                        info.setJobDescription(jobDetail.getDescription());
                        info.setJobStatus(triggerState.name());
                        list.add(info);
                    }
                }
            }
        } catch (SchedulerException e) {
            LOGGER.error("list error", e);
        }
        return list;
    }

    @Override
    public void addJob(EditJobParam editJobParam, OneParamFunction<Void, JobDataMap> fun) {
        ValidatorUtils.validate(editJobParam);
        String jobName = editJobParam.getJobName();
        String jobGroup = editJobParam.getJobGroup();
        String cronExpression = editJobParam.getCronExpression();
        String jobDescription = editJobParam.getJobDescription();
        try {
            if (this.checkExists(jobName, jobGroup)) {
                LOGGER.info("add job fail, job already exist, jobGroup:{}, jobName:{}", jobGroup, jobName);
            }
            CronTrigger trigger = this.getTrigger(jobName, jobGroup, cronExpression);
            JobDetail jobDetail = this.getJobDetail(jobName, jobGroup, jobDescription);
            if (fun != null) {
                fun.callback(jobDetail.getJobDataMap());
            }
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException | ClassNotFoundException e) {
            LOGGER.error("类名不存在或执行表达式错误, exception:{}", e.getMessage(), e);
        }
    }

    @Override
    public void edit(EditJobParam editJobParam, OneParamFunction<Void, JobDataMap> fun) {
        ValidatorUtils.validate(editJobParam);
        String jobName = editJobParam.getJobName();
        String jobGroup = editJobParam.getJobGroup();
        String cronExpression = editJobParam.getCronExpression();
        String jobDescription = editJobParam.getJobDescription();
        try {
            if (!this.checkExists(jobName, jobGroup)) {
                LOGGER.info("edit job fail, job is not exist, jobGroup:{}, jobName:{}", jobGroup, jobName);
            }
            CronTrigger cronTrigger = this.getTrigger(jobName, jobGroup, cronExpression);

            JobKey jobKey = new JobKey(jobName, jobGroup);
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            jobDetail.getJobBuilder().withDescription(jobDescription);

            if (fun != null) {
                fun.callback(jobDetail.getJobDataMap());
            }
            HashSet<Trigger> triggerSet = new HashSet<>();
            triggerSet.add(cronTrigger);
            scheduler.scheduleJob(jobDetail, triggerSet, true);
        } catch (SchedulerException e) {
            LOGGER.error("类名不存在或执行表达式错误, exception:{}", e.getMessage(), e);
        }
    }

    @Override
    public void delete(String jobName, String jobGroup) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        try {
            if (this.checkExists(jobName, jobGroup)) {
                scheduler.pauseTrigger(triggerKey);
                scheduler.unscheduleJob(triggerKey);
                LOGGER.info("delete job, triggerKey:{},jobGroup:{}, jobName:{}", triggerKey, jobGroup, jobName);
            }
        } catch (SchedulerException e) {
            LOGGER.error(e.getMessage());
        }
    }

    @Override
    public void pause(String jobName, String jobGroup) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        try {
            if (this.checkExists(jobName, jobGroup)) {
                scheduler.pauseTrigger(triggerKey);
                LOGGER.info("pause job success, triggerKey:{},jobGroup:{}, jobName:{}", triggerKey, jobGroup, jobName);
            }
        } catch (SchedulerException e) {
            LOGGER.error("pause error", e);
        }
    }

    @Override
    public void resume(String jobName, String jobGroup) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);

        try {
            if (this.checkExists(jobName, jobGroup)) {
                scheduler.resumeTrigger(triggerKey);
                LOGGER.info("resume job success,triggerKey:{},jobGroup:{}, jobName:{}", triggerKey, jobGroup, jobName);
            }
        } catch (SchedulerException e) {
            LOGGER.error("resume error", e);
        }
    }

    @Override
    public boolean checkExists(String jobName, String jobGroup) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        return scheduler.checkExists(triggerKey);
    }

    private CronTrigger getTrigger(String jobName, String jobGroup, String cronExpression) {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
                .cronSchedule(cronExpression)
                .withMisfireHandlingInstructionDoNothing();

        Date now = new Date();
        String createTime = DateFormatUtils.format(now, "yyyy-MM-dd HH:mm:ss");

        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);

        return TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withDescription(createTime)
                .withSchedule(cronScheduleBuilder)
                .build();
    }

    private JobDetail getJobDetail(String jobName, String jobGroup, String jobDescription) throws ClassNotFoundException {
        Class<? extends Job> clazz = (Class<? extends Job>) Class.forName(jobName);
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        return JobBuilder
                .newJob(clazz)
                .withIdentity(jobKey)
                .withDescription(jobDescription)
                .build();
    }
}
